﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using EventFlowLibrary.GameStructure;
using BEPUphysics.Paths.PathFollowing;

namespace EventFlowLibrary.LevelComponents
{
    internal class BaseModel
    {
        /// <summary>
        /// unique ID of the model
        /// </summary>
        internal string ID { get { return id; } }
        private string id;

        /// <summary>
        /// The display model for the static model.
        /// </summary>
        internal Model DisplayModel { get { return displayModel; } }
        private Model displayModel;

        /// <summary>
        /// The world matrix for a model.
        /// </summary>
        internal Matrix WorldMatrix { get { return worldMatrix; } set { worldMatrix = value; } }
        private Matrix worldMatrix = Matrix.Identity;

        /// <summary>
        /// Model position
        /// </summary>
        internal Vector3 Position
        {
            get { return position; }
        }
        private Vector3 position;

        /// <summary>
        /// Model scale
        /// </summary>
        internal Vector3 Scale
        {
            get { return scale; }
        }
        private Vector3 scale;

        /// <summary>
        /// Model rotation
        /// </summary>
        internal Vector3 Rotation
        {
            get { return rotation; }
        }
        private Vector3 rotation;

        /// <summary>
        /// Bounding box of the model after any translations/scales/etc to the model it is part of
        /// </summary>
        internal BoundingBox TransformedBoundingBox { get { return transformedBoundingBox; } set { transformedBoundingBox = value; } }
        private BoundingBox transformedBoundingBox;

        /// <summary>
        /// This tracks the last frame this item was drawn. If drawn in the current frame, it will not draw again.
        /// Used to track models that exist in multiple nodes of the HSG.
        /// </summary>
        public int LastFrameDrawn = -1;

        /// <summary>
        /// List of event boxes tied to this model
        /// </summary>
        private List<EventBox> eventBoxes;

        /// <summary>
        /// Which nodes this model belongs to in our HSG.
        /// </summary>
        private List<int> nodesBelongingTo;

        /// <summary>
        /// Whether the model has moved or not. Used for save/load trimming.
        /// </summary>
        public bool HasMoved { get { return hasMoved; } }
        private bool hasMoved = false;

        /// <summary>
        /// Base model format
        /// </summary>
        /// <param name="_id"></param>
        /// <param name="_model"></param>
        internal BaseModel(string _id, Model _model, Vector3 _scale, Vector3 _rotation, Vector3 _position)
        {
            id = _id;
            displayModel = _model;

            scale = _scale;
            rotation = _rotation;
            position = _position;

            worldMatrix = Matrix.CreateScale(scale) *
                Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y) * Matrix.CreateRotationZ(rotation.Z) *
                Matrix.CreateTranslation(position);

            transformedBoundingBox = LevelSupport.CreateBoundingBox(displayModel, worldMatrix);

            eventBoxes = new List<EventBox>();
            nodesBelongingTo = new List<int>(1);
        }

        /// <summary>
        /// Create a virtual function - models that have "physics movers" need more work than the base model
        /// </summary>
        /// <param name="_position"></param>
        internal virtual void SetPosition(Vector3 _position)
        {
            hasMoved = true;

            position = _position;
            worldMatrix = Matrix.CreateScale(scale) *
                          Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y) * Matrix.CreateRotationZ(rotation.Z) *
                          Matrix.CreateTranslation(position);

            // Translate box according to models position based on our original bounding box
            for (int i = 0; i < eventBoxes.Count; i++)
            {
                eventBoxes[i].Box = new BoundingBox(Vector3.Transform(eventBoxes[i].OriginalBox.Min, WorldMatrix),
                    Vector3.Transform(eventBoxes[i].OriginalBox.Max, WorldMatrix));
            }
        }

        internal virtual void SetRotation(Vector3 _rotation)
        {
            rotation = _rotation;

            hasMoved = true;

            worldMatrix = Matrix.CreateScale(scale) *
                          Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y) * Matrix.CreateRotationZ(rotation.Z) *
                          Matrix.CreateTranslation(position);

            // Translate box according to models position based on our original bounding box
            for (int i = 0; i < eventBoxes.Count; i++)
            {
                eventBoxes[i].Box = new BoundingBox(Vector3.Transform(eventBoxes[i].OriginalBox.Min, WorldMatrix),
                    Vector3.Transform(eventBoxes[i].OriginalBox.Max, WorldMatrix));
            }

            // TODO mover
        }

        internal virtual void SetScale(Vector3 _scale)
        {
            scale = _scale;

            hasMoved = true;

            worldMatrix = Matrix.CreateScale(scale) *
                          Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y) * Matrix.CreateRotationZ(rotation.Z) *
                          Matrix.CreateTranslation(position);

            // Translate box according to models position based on our original bounding box
            for (int i = 0; i < eventBoxes.Count; i++)
            {
                eventBoxes[i].Box = new BoundingBox(Vector3.Transform(eventBoxes[i].OriginalBox.Min, WorldMatrix),
                    Vector3.Transform(eventBoxes[i].OriginalBox.Max, WorldMatrix));
            }

            // TODO mover
        }

        /// <summary>
        /// Add an event box to the model.
        /// </summary>
        /// <remarks>
        /// Events are made in the level editor based in a particular location, and translated according to the model's translation in the editor/events.
        /// So if a large model is centered at 0,0,0 and has three events in various locations, when the model changes location, the events change with it
        /// </remarks>
        internal void AddEventBox(EventBox _box)//, List<string> _states, List<GameEvent> _events)
        {
            // Track the box
            eventBoxes.Add(_box);

            // Translate box according to models position based on our original bounding box
            eventBoxes[eventBoxes.Count - 1].Box = new BoundingBox(Vector3.Transform(eventBoxes[eventBoxes.Count - 1].OriginalBox.Min, Matrix.CreateTranslation(position)),
                        Vector3.Transform(eventBoxes[eventBoxes.Count - 1].OriginalBox.Max, Matrix.CreateTranslation(position)));
        }
    }
}
